package studio.raptor.dispatcher.common.util;

import com.google.common.base.Preconditions;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程管理
 * @author jack
 * @since 1.0
 */
public class ExecutorUtil {

  public static final Map<String/* name */, ExecutorService> executors =
      new ConcurrentHashMap<String, ExecutorService>();


  public static void shutdownAll() {
    for (ExecutorService executor : executors.values()) {
      executor.shutdown();
    }
  }


  public static final ExecutorService createSingleThreadPool(String name) {
    Preconditions.checkArgument(!executors.containsKey(name), "%s executor exists", name);
    ExecutorService executorService =
        Executors.newSingleThreadExecutor(new NameableThreadFactory(name, true));
    executors.put(name, executorService);
    return executorService;
  }


  public static final ExecutorService createFixedThreadPool(String name, int size) {
    return createFixedThreadPool(name, size, true);
  }


  public static final ExecutorService createFixedThreadPool(String name, int size, boolean isDaemon) {
    Preconditions.checkArgument(!executors.containsKey(name), "%s executor exists", name);
    ExecutorService executorService =
        Executors.newFixedThreadPool(size, new NameableThreadFactory(name, isDaemon));
    executors.put(name, executorService);
    return executorService;
  }


  public static final ExecutorService createCachedThreadPool(String name) {
    return createCachedThreadPool(name, true);
  }


  public static final ExecutorService createCachedThreadPool(String name, boolean isDaemon) {
    Preconditions.checkArgument(!executors.containsKey(name), "%s executor exists", name);
    ExecutorService executorService =
        Executors.newCachedThreadPool(new NameableThreadFactory(name, isDaemon));
    executors.put(name, executorService);
    return executorService;
  }


  public static final ExecutorService createCachedThreadPool(String name, int coreSize, int maxSize) {
    Preconditions.checkArgument(!executors.containsKey(name), "%s executor exists", name);
    ExecutorService executorService = new ThreadPoolExecutor(coreSize, maxSize, 30L, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(), // 线程数可伸缩
        new NameableThreadFactory(name, true), new ThreadPoolExecutor.CallerRunsPolicy());
    executors.put(name, executorService);
    return executorService;
  }


  public static final ScheduledExecutorService createSingleThreadScheduledExecutor(String name) {
    Preconditions.checkArgument(!executors.containsKey(name), "%s executor exists", name);
    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(
        new NameableThreadFactory(name, false));
    executors.put(name, executorService);
    return executorService;
  }

  private static class NameableThreadFactory implements ThreadFactory {

    private final ThreadGroup group;
    private final String namePrefix;
    private final boolean isDaemon;
    private final AtomicInteger threadId;


    public NameableThreadFactory(String name, boolean isDaemon) {
      SecurityManager s = System.getSecurityManager();
      this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
      this.namePrefix = name;
      this.threadId = new AtomicInteger(0);
      this.isDaemon = isDaemon;
    }


    @Override
    public Thread newThread(Runnable r) {
      Thread t = new Thread(group, r, namePrefix + threadId.getAndIncrement());
      t.setDaemon(isDaemon);
      if (t.getPriority() != Thread.NORM_PRIORITY) {
        t.setPriority(Thread.NORM_PRIORITY);
      }
      return t;
    }

  }

}
